<chapter xml:id="logger.h">
<title><tt>__vic/logger.h</tt></title>

<chapter xml:id="logger">
<title><tt>logger</tt></title>

<code-block lang="C++"><![CDATA[
class logger : private non_copyable
{
public:
    enum class severity : unsigned char
    {
        trace,
        debug,
        info,
        notice,
        warning,
        error,
        fatal
    };
    using severity_t = severity; // use this alias as a type name
    struct output
    {
        virtual void publish_record(severity_t , const char * , size_t ) = 0;
    protected:
        ~output() = default;
    };
    class settings_t
    {
        struct output &output() const;
        severity_t level() const;
    };
    class record;

    explicit logger(output &out, severity_t = severity::info);
    explicit logger(settings_t s);
    ~logger();

    severity_t level() const;
    void level(severity_t new_level);
    settings_t settings() const;
    output &reset_output(output &out);
    output &get_output();
    const output &get_output() const;

    static constexpr size_t min_buffer_size = ...;
    void shrink_buffer(size_t limit);

    void message(severity_t severity, const char *msg, size_t msg_len);
#if __cpp_lib_string_view // C++17
    void message(severity_t severity, std::string_view msg);

    void trace(std::string_view msg);
    void debug(std::string_view msg);
    void info(std::string_view msg);
    void notice(std::string_view msg);
    void warning(std::string_view msg);
    void error(std::string_view msg);
    void fatal(std::string_view msg);
#else // until C++17
    void message(severity_t severity, const char *msg);
    void message(severity_t severity, const std::string &msg);

    void trace(const char *msg);
    void debug(const char *msg);
    void info(const char *msg);
    void notice(const char *msg);
    void warning(const char *msg);
    void error(const char *msg);
    void fatal(const char *msg);

    void trace(const std::string &msg);
    void debug(const std::string &msg);
    void info(const std::string &msg);
    void notice(const std::string &msg);
    void warning(const std::string &msg);
    void error(const std::string &msg);
    void fatal(const std::string &msg);
#endif

#if __cpp_lib_format >= 202106L // C++20 + P2508
    template<class... Args>
    void format(severity_t s,
        std::format_string<Args...> fmt, Args&&... args);

    template<class Arg1, class... Args>
    void trace(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void debug(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void info(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void notice(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void warning(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void error(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void fatal(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
#endif

    record trace();
    record debug();
    record info();
    record notice();
    record warning();
    record error();
    record fatal();

    bool trace_visible() const;
    bool debug_visible() const;
    bool info_visible() const;
    bool notice_visible() const;
    bool warning_visible() const;
    bool error_visible() const;
    bool fatal_visible() const;
};
class logger::record
{
public:
    record(logger &log, severity_t sev);
    ~record();

    record append(const char *str, size_t str_len);

    template<class T> record operator<<(const T &v);
};
template<class T> struct log_value
{
    static void to_text(const T &v, std::string &s);
};
const char *to_string(logger::severity_t s);
#if __cpp_lib_string_view // C++17
constexpr std::string_view to_string_view(logger::severity s);
#endif
]]></code-block>

<p>Front-end логгера. Реализует построение записей с помощью оператора
<tt>&lt;&lt;</tt>, подобно стандартной библиотеке потоков <tt>iostream</tt>.
Каждая запись лога имеет назначенный приоритет. Логгер может фильтровать вывод
записей по этому приоритету. Предопределены 6 уровней приоритета (в порядке
возрастания):</p>
<list style="numbered">
    <item>TRACE - подробная отладка,</item>
    <item>DEBUG - отладка,</item>
    <item>INFO - информационное сообщение,</item>
    <item>NOTICE - нормальное, но важное событие,</item>
    <item>WARNING - незначительная ошибка или подозрительная ситуация,</item>
    <item>ERROR - серьёзная ошибка, но приложение может продолжать
        работу,</item>
    <item>FATAL - критическая ошибка, приложение не может продолжить
        выполнение.</item>
</list>

<p>INFO является уровнем логирования по умолчанию. Если сообщение (запись)
имеет приоритет ниже текущего уровня логирования, то оно игнорируется и никуда
не выводится.</p>

<p>Для создания сообщений с нужным приоритетом предоставляется набор одноимённых
функций. Например, <tt>info()</tt> для сообщений INFO. Также есть универсальная
функция <tt>message()</tt>, в которой приоритет задаётся параметром, но, обычно,
следует использовать специфичные функции.</p>

<p>Существует 2 способа создания сообщений. Первый прост и обычен:</p>
<code-block lang="C++">
log.trace("Trace message");
log.debug("Debug message");
log.info("Info message");
log.notice("Notice");
log.warning("Warning");
log.error("Recoverable error");
log.fatal("Fatal error");
</code-block>

<p>Второй немного более сложен, но предоставляет гораздо большие возможности:</p>
<code-block lang="C++"><![CDATA[
log.error() << "Cannot open file " << filename << '!';
log.warning() << "Loop iteration no " << i;
]]></code-block>

<p>Вызов функции без параметров создаёт объект типа <tt>logger::record</tt> с
соответствующим приоритетом. Теперь в него можно писать сообщение с
использованием оператора <tt>&lt;&lt;</tt>. Сформированная запись будет
выведена в лог по завершении вычисления «полного выражения» (термин из
Стандарта)</p>

<p>Если сформировать запись лога сложно или невозможно одним выражением, то
следует явно создать объект <tt>logger::record</tt> и писать в него. Запись
будет выведена с лог при вызове деструктора данного объекта:</p>
<code-block lang="C++"><![CDATA[
{
    logger::record rec = log.info(); // Начало формирования записи
    rec << "List elements: ";
    for(auto el : list) rec << el << ", ";
    // Сформированная запись попадёт в лог при выходе из блока
}
]]></code-block>

<note>В целях повышения эффективности используйте обычную функциональную запись,
если строка уже готова для вывода:</note>
<code-block lang="C++"><![CDATA[
log.info("Message");
// а не
log.info() << "Message";
]]></code-block>

<p>Вывод записей с приоритетами DEBUG и TRACE обычно отключен в нормальных
условиях. Такие записи хоть и не попадут в лог, но время на их форматирование
всё равно будет тратиться. Поэтому перед попыткой сформировать какую-нибудь
отладочную запись с помощью операторов <tt>&lt;&lt;</tt> проверьте, включена ли
отладка, с помощью вызова <tt>debug_visible()</tt> или <tt>trace_visible()</tt>:
</p>
<code-block lang="C++"><![CDATA[
if(log.debug_visible())
    log.debug() << ...; // формируем запись
]]></code-block>
<p>Это не относится к обычным вызовам <tt>debug(msg)</tt> и <tt>trace(msg)</tt>,
которым передаётся уже готовая к выводу строка, и никакого дополнительного
форматирования не требуется.</p>

<p>Для использования <tt>logger</tt> нужно реализовать абстрактный базовый класс
<tt>logger::output</tt> (определить <tt>publish_record()</tt>). Реализация
должна вывести куда-то сформированную запись, например, в файл или БД.
<tt>output</tt>, переданный в <tt>logger</tt> при конструировании, впоследствии
можно заменить с помощью вызова <tt>reset_output()</tt>.</p>

<section><title>Члены класса</title>

<synopsis>
<prototype>severity::trace</prototype>
<prototype>severity::debug</prototype>
<prototype>severity::info</prototype>
<prototype>severity::notice</prototype>
<prototype>severity::warning</prototype>
<prototype>severity::error</prototype>
<prototype>severity::fatal</prototype>
<p>Константы приоритетов и уровней логирования. Данная форма используется как в
режиме C++11, так и в C++98.</p>
</synopsis>

<synopsis>
<prototype>typename severity_t</prototype>
<p>Используйте данный идентификатор, если коду требуется совместимость с режимом
C++98. Начиная с C++11 это просто синоним <tt>severity</tt>.</p>
</synopsis>

<synopsis>
<prototype>class output</prototype>
<p>Интерфейс back-end'а логирования.</p>
</synopsis>

<synopsis>
<prototype>void output::publish_record(severity_t sev, const char *buf, size_t buf_len)</prototype>
<p>Реализация этой чисто виртуальной функции должна вывести содержимое
<tt>buf</tt> длиной <tt>buf_len</tt> в лог как одну запись. Функция вызывается
только когда переданный <tt>sev >= level()</tt>. Реализация может полагаться на
это предусловие.</p>
</synopsis>

<synopsis>
<prototype>class settings_t</prototype>
<p>Хранит настройки логгера: уровень логирования и ссылку на вывод
(<tt>level()</tt> + <tt>get_output()</tt>).</p>
</synopsis>

<synopsis>
<prototype>explicit logger(output &amp;out, severity_t level = severity::info)</prototype>
<p>Создаёт логгер с данным выводом и уровнем логирования. Время жизни объекта,
на который ссылается <tt>out</tt>, должно превосходить время жизни логгера!</p>
<postcondition><tt>this->level() == level &amp;&amp; &amp;this->get_output() == &amp;out</tt></postcondition>
</synopsis>

<synopsis>
<prototype>explicit logger(settings_t s)</prototype>
<p>Создаёт логгер с указанными настройками.</p>
</synopsis>

<synopsis>
<prototype>severity_t level() const</prototype>
<p>Возвращает текущий уровень логирования.</p>
</synopsis>

<synopsis>
<prototype>void level(severity_t new_level)</prototype>
<p>Устанавливает уровень логирования.</p>
<postcondition><tt>level() == new_level</tt></postcondition>
</synopsis>

<synopsis>
<prototype>settings_t settings() const</prototype>
<p>Возвращает текущие настройки.</p>
</synopsis>

<synopsis>
<prototype>output &amp;reset_output(output &amp;out)</prototype>
<p>Устанавливает новый вывод и возвращает старый.</p>
<postcondition><tt>&amp;get_output() == &amp;out</tt></postcondition>
</synopsis>

<synopsis>
<prototype>output &amp;get_output()</prototype>
<prototype>const output &amp;get_output() const</prototype>
<p>Возвращает ссылку на текущий вывод.</p>
</synopsis>

<synopsis>
<prototype>static constexpr size_t min_buffer_size</prototype>
<p>Минимальный размер внутреннего буфера в байтах.</p>
</synopsis>

<synopsis>
<prototype>void shrink_buffer(size_t limit)</prototype>
<p>Устанавливает размер внутреннего буфера в <tt>min_buffer_size</tt>, если он
превосходит <tt>limit</tt> байтов. Позволяет предотвратить бесконрольное
разрастание используемой памяти при выводе записей большого размера.</p>
</synopsis>

<synopsis>
<prototype>void message(severity_t severity, const char *msg, size_t msg_len)</prototype>
<prototype>void message(severity_t severity, std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void message(severity_t severity, const char *msg) <badge>until C++17</badge></prototype>
<prototype>void message(severity_t severity, const std::string &amp;msg) <badge>until C++17</badge></prototype>
<p>Выводит сообщение с заданным приоритетом.</p>
</synopsis>

<synopsis>
<prototype>void trace(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void trace(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void trace(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void debug(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void debug(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void debug(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void info(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void info(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void info(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void notice(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void notice(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void notice(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void warning(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void warning(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void warning(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void error(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void error(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void error(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void fatal(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void fatal(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void fatal(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<p>Выводит сообщение с соответствующим приоритетом.</p>
</synopsis>

<synopsis>
<prototype><![CDATA[template<class... Args>
void format(severity_t s,
    std::format_string<Args...> fmt, Args&&... args)]]> <badge>C++20</badge></prototype>
<p>Форматирует сообщение используя указанную строку формата и аргументы
(подобно <tt>std::format</tt>), затем выводит его с заданным приоритетом.</p>
</synopsis>

<synopsis>
<prototype><![CDATA[template<class Arg1, class... Args>
void trace(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void debug(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void info(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void notice(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void warning(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void error(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void fatal(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<p>Форматирует сообщение используя указанную строку формата и аргументы
(подобно <tt>std::format</tt>), затем выводит его с соответствующим
приоритетом.</p>
</synopsis>

<synopsis>
<prototype>logger::record trace()</prototype>
<prototype>logger::record debug()</prototype>
<prototype>logger::record info()</prototype>
<prototype>logger::record notice()</prototype>
<prototype>logger::record warning()</prototype>
<prototype>logger::record error()</prototype>
<prototype>logger::record fatal()</prototype>
<p>Создаёт новую запись c соответствующим приоритетом и позволяет писать в неё
части сообщения с помощью оператора <tt>&lt;&lt;</tt>.</p>
</synopsis>

<synopsis>
<prototype>bool trace_visible() const</prototype>
<prototype>bool debug_visible() const</prototype>
<prototype>bool info_visible() const</prototype>
<prototype>bool notice_visible() const</prototype>
<prototype>bool warning_visible() const</prototype>
<prototype>bool error_visible() const</prototype>
<prototype>bool fatal_visible() const</prototype>
<p>Возвращает <tt>true</tt>, если запись с указанным уровнем логирования будет
выведена в лог при текущих настройках. Использование данных функций позволяет
исключить форматирование сообщений, которые всё равно не попадут в лог.</p>
</synopsis>

<synopsis>
<prototype>record::record(logger &amp;log, severity_t sev)</prototype>
<p>Создаёт запись лога с указанным приоритетом. Обычно вместо явного вызова
данного конструктора следует использовать функции <tt>logger</tt>’а, вроде
<tt>info()</tt> без параметров, для создания объекта.</p>
</synopsis>

<synopsis>
<prototype>record::~record()</prototype>
<p>Выводит сформированную запись в лог.</p>
</synopsis>

<synopsis>
<prototype>record record::append(const char *str, size_t str_len)</prototype>
<p>Добавляет строку к сообщению.</p>
</synopsis>

<synopsis>
<prototype>template&lt;class T> record record::operator&lt;&lt;(const T &amp;v)</prototype>
<p>Набор инсертеров для различных типов данных. Указанное значение преобразуется
в текст с использованием вызова <tt>log_value&lt;T>::to_text()</tt>.</p>
</synopsis>

</section>

<section><title>Преобразователи значений в текст</title>

<synopsis>
<prototype>template&lt;class T> void log_value&lt;T>::to_text(const T &amp;v, std::string &amp;s)</prototype>
<p>Преобразует значение типа <tt>T</tt> в текст, используя unqualified вызов
<xref to="to_text_append"/>.</p>
<note>Данная функция (точнее класс со статической функцией) может быть
спецализирована для Вашего <tt>T</tt>. Однако, обычно, Вам следует просто
определить <tt>to_text_append(const T &amp;, std::string &amp;)</tt>.</note>
</synopsis>

</section>

<section><title>Свободные функции</title>

<synopsis>
<prototype>const char *to_string(logger::severity_t s)</prototype>
<prototype>constexpr std::string_view to_string_view(logger::severity s) <badge>C++17</badge></prototype>
<p>Преобразует приоритет записи лога в текстовое представление, которое можно,
например, вывести в лог. Пример: для <tt>severity::debug</tt> возвращается
"<tt>DEBUG</tt>" и т.п.</p>
</synopsis>

</section>

<section><title>Пример</title>

<code-block lang="C++"><![CDATA[
/////////////////////////////////////////////////////////////////////////////
// Вывод сообщений в std::clog с указанием приоритета
class coutput : public __vic::logger::output
{
public:
    void publish_record(__vic::logger::severity_t s,
                            const char *rec, size_t rec_n)
    {
        std::clog << to_string(s) << ": ";
        std::clog.write(rec, rec_n) << std::endl;
    }
};
/////////////////////////////////////////////////////////////////////////////

int main()
{
    coutput log_output:
    __vic::logger log(log_output, __vic::logger::severity::debug);

    log.info("Application is started");

    for(int i = 0; i < 5; i++)
        log.debug() << "Loop i = " << i;

    log.warning("Application end");
}
]]></code-block>

<p>Результат:</p>

<tty>
INFO: Application is started
DEBUG: Loop i = 0
DEBUG: Loop i = 1
DEBUG: Loop i = 2
DEBUG: Loop i = 3
DEBUG: Loop i = 4
WARNING: Application end
</tty>

</section>

</chapter>

</chapter>
